{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "d4d79832-59ab-410a-ad6d-fbba01a3f0d3",
   "metadata": {},
   "source": [
    "# Intro to Data Readers\n",
    "Within the Data Profiler, there are 5 data reader classes:\n",
    "\n",
    "  * CSVData (delimited data: CSV, TSV, etc.)\n",
    "  * JSONData\n",
    "  * ParquetData\n",
    "  * AVROData\n",
    "  * GraphData\n",
    "  * TextData\n",
    "  \n",
    "Each of these classes can be used to read data individually, however the Data Profiler provides the unique capability of auto detecting what data you have and reading it automatically by using the `Data` class.\n",
    "```python\n",
    "import dataprofiler as dp\n",
    "data = dp.Data('/path/to/mydata.abc')  # auto detects and reads your data\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f2315666-20be-4937-9f9a-26d42dc135e2",
   "metadata": {
    "tags": []
   },
   "source": [
    "## Automatically reading and detecting data\n",
    "\n",
    "Below is a demonstration of utilizing the `Data` class which automatically detects the type of data for a given file and reads it automatically."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "99e61c6c-43b8-4700-b627-759b5ef8bdda",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import sys\n",
    "\n",
    "try:\n",
    "    sys.path.insert(0, '..')\n",
    "    import dataprofiler as dp\n",
    "except ImportError:\n",
    "    import dataprofiler as dp"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8821ad8d-b2c0-489c-ae6a-54c11b7f0a08",
   "metadata": {},
   "outputs": [],
   "source": [
    "# use data reader to read input data with different file types\n",
    "data_folder = \"../dataprofiler/tests/data\"\n",
    "csv_files = [\n",
    "    \"csv/aws_honeypot_marx_geo.csv\",\n",
    "    \"csv/all-strings-skip-header-author.csv\", # csv files with the author/description on the first line\n",
    "    \"csv/sparse-first-and-last-column-empty-first-row.txt\", # csv file with the .txt extension\n",
    "]\n",
    "json_files = [\n",
    "    \"json/complex_nested.json\",\n",
    "    \"json/honeypot_intentially_mislabeled_file.csv\", # json file with the .csv extension\n",
    "]\n",
    "parquet_files = [\n",
    "    \"parquet/nation.dict.parquet\",\n",
    "    \"parquet/nation.plain.intentionally_mislabled_file.csv\", # parquet file with the .csv extension\n",
    "]\n",
    "avro_files = [\n",
    "    \"avro/userdata1.avro\",\n",
    "    \"avro/userdata1_intentionally_mislabled_file.json\", # avro file with the .json extension\n",
    "]\n",
    "graph_files = [\n",
    "    \"csv/graph_data_csv_identify.csv\", # csv file with graph column names\n",
    "]\n",
    "text_files = [\n",
    "    \"txt/discussion_reddit.txt\",\n",
    "]\n",
    "all_files = csv_files + json_files + parquet_files + avro_files + graph_files + text_files\n",
    "print('filepath' + ' ' * 58 + 'data type')\n",
    "print('='*80)\n",
    "for file in all_files:\n",
    "    filepath = os.path.join(data_folder, file)\n",
    "    data = dp.Data(filepath)\n",
    "    print(\"{:<65} {:<15}\".format(file, data.data_type))\n",
    "print(\"\\n\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "49dfc981-59fd-48a5-ad7b-e01f0a52d0b2",
   "metadata": {},
   "outputs": [],
   "source": [
    "# importing from a url\n",
    "data = dp.Data('https://raw.githubusercontent.com/capitalone/DataProfiler/main/dataprofiler/tests/data/csv/diamonds.csv')\n",
    "data.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "77f8ef2d-5aaf-44d6-b6d1-bf14f7eb7aa6",
   "metadata": {},
   "source": [
    "## Specifying detection options of `Data` and loading `pandas.DataFrame`\n",
    "\n",
    "The `Data` class also gives the ability to set options or if the user wants to load their data with specific requirements.\n",
    "Options for each data reader are specified in the docs: https://capitalone.github.io/DataProfiler/docs/0.4.4/html/dataprofiler.data_readers.html\n",
    "\n",
    "```python\n",
    "import dataprofiler as dp\n",
    "\n",
    "options = {...}  # allowed options are specified for each data reader.\n",
    "data = dp.Data(data, options=options)\n",
    "```\n",
    "Later in this tutorial, the options for the CSVData class will be discussed.\n",
    "\n",
    "Additionally, a user can directly load a `pandas.DataFrame` as any data reader they choose."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4b925d4e-ca94-4913-9acf-26a883585e85",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "from dataprofiler.data_readers.csv_data import CSVData\n",
    "\n",
    "\n",
    "df = pd.DataFrame(['my', 'random', 'data'])\n",
    "\n",
    "# specify via the `Data` class\n",
    "data = dp.Data(data=df, data_type='csv')\n",
    "print('Data Type: ', data.data_type)\n",
    "\n",
    "# specifically use the CSVData class\n",
    "data = CSVData(data=df)\n",
    "print('Data Type: ', data.data_type)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "52c3c3ac-c241-4d91-8ac7-b3d28ffd19c3",
   "metadata": {},
   "source": [
    "## Accessing data and attributes\n",
    "\n",
    "Once loaded, the data can be accessed via the `data` property of the object. Additional information about the data loaded may differ between data readers.\n",
    "\n",
    "For this example we will focus on `CSVData`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "09fa5929-e710-4107-9313-1370ab639c9c",
   "metadata": {},
   "outputs": [],
   "source": [
    "filepath = \"../dataprofiler/tests/data/csv/aws_honeypot_marx_geo.csv\"\n",
    "data = dp.Data(filepath)\n",
    "print('Data Type: ', data.data_type)\n",
    "print('Data Filepath: ', data.input_file_path)\n",
    "print('File Encoding: ', data.file_encoding)\n",
    "print('Data Length (two techniques): ', len(data), data.length)\n",
    "print(\"Data Access:\")\n",
    "data.data"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b98be971-4768-479d-9e54-00f05a6fb790",
   "metadata": {},
   "source": [
    "## Checking data file types with `is_match`\n",
    "\n",
    "Each data reader has a class method `is_match` which determines whether or not a dataset is of a given data type.\n",
    "```python\n",
    "CSVData.is_match\n",
    "JSONData.is_match\n",
    "ParquetData.is_match\n",
    "AVROData.is_match\n",
    "GraphData.is_match\n",
    "TextData.is_match\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "104a32c1-1d50-4aa5-94ce-b2e72de38476",
   "metadata": {},
   "outputs": [],
   "source": [
    "# supplemental function\n",
    "def add_true_false_color(value):\n",
    "    \"\"\"Converts True to green and False to red in printed text.\"\"\"\n",
    "    if value:\n",
    "        return \"\\x1b[92m  \" + str(is_match) + \"\\x1b[0m\"\n",
    "    return \"\\x1b[31m \" + str(is_match) + \"\\x1b[0m\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "06868d90-2726-4096-a6da-3866174e6671",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from dataprofiler.data_readers.csv_data import CSVData\n",
    "\n",
    "\n",
    "non_csv_files = [\n",
    "    'json/iris-utf-8.json',\n",
    "    'json/honeypot_intentially_mislabeled_file.csv',\n",
    "    'parquet/titanic.parq',\n",
    "    'parquet/nation.plain.intentionally_mislabled_file.csv',\n",
    "    'txt/code.txt',\n",
    "    'txt/sentence.txt',\n",
    "    'avro/users.avro',\n",
    "    'avro/snappy_compressed_intentionally_mislabeled_file.csv',\n",
    "]\n",
    "\n",
    "print(\"Is the file a CSV?\")\n",
    "print('=' * 80)\n",
    "for file in csv_files:\n",
    "    filepath = os.path.join(data_folder, file)\n",
    "    is_match = CSVData.is_match(filepath)\n",
    "    print(add_true_false_color(is_match), ':', file)\n",
    "    print('=' * 80)\n",
    "    \n",
    "for file in non_csv_files:\n",
    "    filepath = os.path.join(data_folder, file)\n",
    "    is_match = CSVData.is_match(filepath)\n",
    "    print(add_true_false_color(is_match), ':', file)\n",
    "    print('=' * 80)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "38889990-8e19-4114-a4f3-dc2af938e29d",
   "metadata": {},
   "source": [
    "## Reloading data after altering options with `reload`\n",
    "\n",
    "There are two cases for using the reload function, both of which require the data type to have been interpreted correctly:\n",
    "\n",
    "    1. The options were not correctly determined\n",
    "    2. The options were loaded correctly but a change is desired.\n",
    "    \n",
    "In the example below, the `data_format` for reading the data is changed and the data is then reloaded."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "01870e8d-45ee-4f33-a088-4453c7ffc7c2",
   "metadata": {},
   "outputs": [],
   "source": [
    "filepath = \"../dataprofiler/tests/data/csv/diamonds.csv\"\n",
    "\n",
    "data = dp.Data(filepath)\n",
    "print('original data:')\n",
    "print('=' * 80)\n",
    "print(data.data[:5])\n",
    "\n",
    "print()\n",
    "data.reload(options={'data_format': 'records', 'record_samples_per_line': 1})\n",
    "print('reloaded data:')\n",
    "print('=' * 80)\n",
    "data.data[:5]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e2285f19-9b34-4484-beaa-79df890b2825",
   "metadata": {},
   "source": [
    "## A deeper dive into `CSVData`\n",
    "\n",
    "This next section will focus on how to use the data reader class: `CSVData`. The `CSVData` class is used for reading delimited data. Delimited data are datasets which have their columns specified by a specific character, commonly the `,`. E.g. from the `diamonds.csv` dataset:\n",
    "```\n",
    "carat,cut,color,clarity,depth,table,price,x,y,z\n",
    "0.23,Ideal,E,SI2,61.5,55,326,3.95,3.98,2.43\n",
    "0.21,Premium,E,SI1,59.8,61,326,3.89,3.84,2.31\n",
    "0.23,Good,E,VS1,56.9,65,327,4.05,4.07,2.31\n",
    "0.29,Premium,I,VS2,62.4,58,334,4.2,4.23,2.63\n",
    "0.31,Good,J,SI2,63.3,58,335,4.34,4.35,2.75\n",
    "```\n",
    "\n",
    "However, the delimiter can be any character. Additionally, a `quotechar`, commonly `\"`, can be specified which allows a delimiter to be contained within a column value.\n",
    "E.g. from the `blogposts.csv` dataset:\n",
    "```\n",
    "Blog Post,Date,Subject,Field\n",
    "\"Monty Hall, meet Game Theory\",4/13/2014,Statistics,Mathematics\n",
    "Gaussian Quadrature,4/13/2014,Algorithms,Mathematics\n",
    "```\n",
    "Notice how `\"Monty Hall, meet Game Theory\"` is contained by the quotechar because it contains the delimiter value `,`.\n",
    "\n",
    "These delimiter dataset parameters (and more) can be automatically determined by the `CSVData` data reader, however they can also be set via the options as demonstrated later in this tutorial."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cccb6bf9-7fb8-46b8-992e-9caacb7ab3a8",
   "metadata": {},
   "source": [
    "## Intro to the `CSVData` data reader\n",
    "\n",
    "Previously, it was shown that `CSVData` may automatically be detected using `Data` or can be manually specified by the user:\n",
    "\n",
    "```python\n",
    "import dataprofiler as dp\n",
    "from dataprofiler.data_readers.csv_data import CSVData\n",
    "\n",
    "data = dp.Data(filepath)\n",
    "data = CSVData(filepath)\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e25f5130-4f19-40c5-9d13-549a04f1aef5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# use data reader to read delimited data \n",
    "data_folder = \"../dataprofiler/tests/data\"\n",
    "csv_files = [\n",
    "    \"csv/diamonds.csv\",\n",
    "    \"csv/all-strings-skip-header-author.csv\", # csv files with the author/description on the first line\n",
    "    \"csv/sparse-first-and-last-column-empty-first-row.txt\", # csv file with the .txt extension\n",
    "]\n",
    "\n",
    "for file in csv_files:\n",
    "    data = CSVData(os.path.join(data_folder, file))\n",
    "    print(data.data.head())\n",
    "    print('=' * 80)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8940de56-1417-4bf6-af87-9d4d00b9a631",
   "metadata": {},
   "source": [
    "## CSVData Options\n",
    "\n",
    "As mentioned preivously, `CSVData` has options that can be set to finetune its detection or to ensure the data is being read in a specific manner.\n",
    "The options for `CSVData` are detailed below:\n",
    "\n",
    "  * delimiter - delimiter used to decipher the csv input file\n",
    "  * quotechar - quote character used in the delimited file\n",
    "  * header - location of the header in the file.\n",
    "  * data_format - user selected format in which to return data can only be of specified types\n",
    "  * selected_columns - columns being selected from the entire dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3d74f2e8-0ec3-4e93-8778-0a5f013e0cdb",
   "metadata": {},
   "outputs": [],
   "source": [
    "# options are set via a dictionary object in which the parameters are specified.\n",
    "# these are the default values for each option\n",
    "options = {\n",
    "    \"delimiter\": \",\",\n",
    "    \"quotechar\": '\"',\n",
    "    \"header\": 'auto',\n",
    "    \"data_format\": \"dataframe\",  # type: str, choices: \"dataframe\", \"records\"\n",
    "    \"selected_columns\": list(),\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9af108a1-ffe6-4c3a-82cc-833b1a3b57a1",
   "metadata": {},
   "source": [
    "## Options: delimiter and quotechar\n",
    "\n",
    "Below, both the auto detection and use of options will be illustrated for `delimiter` and `quotechar`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "570e20c3-198e-4356-98d3-92eb9655ef4e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# display the data we are reading\n",
    "filepath = \"../dataprofiler/tests/data/csv/daily-activity-sheet-@-singlequote.csv\"\n",
    "num_lines = 10\n",
    "with open(filepath) as fp:\n",
    "    print(''.join(fp.readlines()[:num_lines]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "98385148-861e-4eb1-ba8d-e93120515401",
   "metadata": {},
   "outputs": [],
   "source": [
    "data = dp.Data(filepath)  # or use CSVData\n",
    "print('Auto detected')\n",
    "print('=' * 80)\n",
    "print('delimiter: ', data.delimiter)\n",
    "print('quotechar: ', data.quotechar)\n",
    "data.data.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7f5d9306-d90a-4fc6-85a7-a0d535fe2d80",
   "metadata": {},
   "outputs": [],
   "source": [
    "options = {'delimiter': '@', 'quotechar': \"'\"}\n",
    "data = dp.Data(filepath, options=options)  # or use CSVData\n",
    "print('manually set')\n",
    "print('=' * 80)\n",
    "print('delimiter: ', data.delimiter)\n",
    "print('quotechar: ', data.quotechar)\n",
    "data.data.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b7bfa60f-b5b9-48a5-adc5-3937aed145da",
   "metadata": {},
   "outputs": [],
   "source": [
    "# intentional failure with incorrect options\n",
    "options = {'delimiter': ',', 'quotechar': '\"'}\n",
    "\n",
    "# will be interepted as TextData because the delimtier and quotechar were incorrect\n",
    "data = dp.Data(filepath, options=options)\n",
    "print('intentional faliure set')\n",
    "print('=' * 80)\n",
    "try:\n",
    "    print('delimiter: ', data.delimiter)  # attribute error raised here, bc TextData, not CSVData\n",
    "    print('quotechar: ', data.quotechar)\n",
    "    \n",
    "    # should not reach this or something went wrong\n",
    "    raise Exception('Should have failed because this is detected as TextData.')\n",
    "except AttributeError:\n",
    "    print('When data_type is not set or the CSVData is not set, it will fail over to the\\n'\n",
    "          'next best reader. In this case it is \"TextData\"\\n')\n",
    "data.data"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eeb41c7c-8319-40a3-9d87-88edbb3c5290",
   "metadata": {},
   "source": [
    "## Options: header\n",
    "\n",
    "Below, both the auto detection and use of options will be illustrated for `header`.\n",
    "\n",
    "Notice how in the manually set mechanism, we are intentionally setting the header incorrectly to illustrate what happens."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "16a927ef-1ba8-4bf2-ae40-2a9909030609",
   "metadata": {},
   "outputs": [],
   "source": [
    "# display the data we are reading\n",
    "filepath = \"../dataprofiler/tests/data/csv/sparse-first-and-last-column-header-and-author-description.txt\"\n",
    "num_lines = 10\n",
    "with open(filepath) as fp:\n",
    "    print(''.join(fp.readlines()[:num_lines]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0701d7bf-2de0-4dce-8f09-7f0cddd1132c",
   "metadata": {},
   "outputs": [],
   "source": [
    "options = {'header': 'auto'}  # auto detected (default value)\n",
    "data = dp.Data(filepath, options=options)  # or use CSVData\n",
    "print('Data Header:', data.header)\n",
    "print('=' * 80)\n",
    "data.data.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b8642a0a-367a-44c6-b611-b89d97b29f85",
   "metadata": {},
   "outputs": [],
   "source": [
    "options = {'header': 2}  # intentionally set incorrectly at value 2\n",
    "data = dp.Data(filepath, options=options)  # or use CSVData\n",
    "print('Data Header:', data.header)\n",
    "print('=' * 80)\n",
    "data.data.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d6e3f640-c809-4eb6-9571-30065821615e",
   "metadata": {},
   "source": [
    "## Options: data_format\n",
    "\n",
    "For CSVData, the `data_format` option can have the following values:\n",
    "\n",
    "  * dataframe - (default) loads the dataset as a pandas.DataFrame\n",
    "  * records   - loads the data as rows of text values, the extra parameter `record_samples_per_line` how many rows are combined into a single line\n",
    "  \n",
    "`dataframe` is used for conducting **structured profiling** of the dataset while `records` is for **unstructured profiling**.\n",
    "\n",
    "Below, both the auto detection and use of options will be illustrated for `data_format`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "146109ea-a554-4766-bb19-78c116d2a8dd",
   "metadata": {},
   "outputs": [],
   "source": [
    "# display the data we are reading\n",
    "filepath = \"../dataprofiler/tests/data/csv/diamonds.csv\"\n",
    "num_lines = 10\n",
    "with open(filepath) as fp:\n",
    "    print(''.join(fp.readlines()[:num_lines]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dceac967-d326-4064-ba1c-87a1146c9d72",
   "metadata": {},
   "outputs": [],
   "source": [
    "options = {'data_format': 'dataframe'}  # default\n",
    "data = dp.Data(filepath, options=options)  # or use CSVData\n",
    "data.data[:5]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2c25524f-ef23-4e06-9023-842c64c2640e",
   "metadata": {},
   "outputs": [],
   "source": [
    "options = {'data_format': 'records', 'record_samples_per_line': 1}\n",
    "data = dp.Data(filepath, options=options)\n",
    "data.data[:5]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d45f3ed6-ddcd-4bf3-95bc-09f23eb94c97",
   "metadata": {},
   "source": [
    "## Options: selected columns\n",
    "\n",
    "By default, all columns of a dataset will be read and loaded into the data reader. However, `selected_columns` can be set to only load columns which the user requests."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c9b45e18-93c6-42e6-b978-af51574307eb",
   "metadata": {},
   "outputs": [],
   "source": [
    "# display the data we are reading\n",
    "filepath = \"../dataprofiler/tests/data/csv/aws_honeypot_marx_geo.csv\"\n",
    "num_lines = 10\n",
    "with open(filepath) as fp:\n",
    "    print(''.join(fp.readlines()[:num_lines]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "018f3f4d-32ac-411a-9918-bae78aff0b0e",
   "metadata": {},
   "outputs": [],
   "source": [
    "options = {'selected_columns': ['datetime', 'host', 'src', 'proto']}\n",
    "data = dp.Data(filepath, options=options)\n",
    "data.data.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b50679ea",
   "metadata": {},
   "source": [
    "## Intro to `GraphData` data reader\n",
    "\n",
    "This tutorial will focus on how to use the data reader class: `GraphData`. The `GraphData` class is used for reading the delimited data from a CSV file into a `NetworkX` Graph object. This is all in an effort to prepare the data automaticaly for `GraphProfiler` class to then profile graph data. \n",
    "\n",
    "The DataProiler keys off of common graph naming conventions in the column header row. E.G. from `dataprofiler/tests/csv/graph_data_csv_identify.csv`\n",
    "```\n",
    "node_id_dst, node_id_src, continuous_weight, categorical_status\n",
    "108,289,7.4448069,9\n",
    "81,180,3.65064207,0\n",
    "458,83,5.9959787,10\n",
    "55,116,4.63359209,79\n",
    "454,177,5.76715529,11\n",
    "429,225,4.79556889,3\n",
    "```\n",
    "\n",
    "Options for the `GraphData` are exactly the same as `CSVData`.\n",
    "\n",
    "\n",
    "Example implementation of `GraphData`:\n",
    "```python\n",
    "import dataprofiler as dp\n",
    "from dataprofiler.data_readers.graph_data import GraphData\n",
    "\n",
    "data = dp.Data(graph_file)\n",
    "data = GraphData(graph_file)\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "838db976",
   "metadata": {},
   "outputs": [],
   "source": [
    "from dataprofiler.data_readers.graph_data import GraphData\n",
    "\n",
    "# use data reader to read delimited data \n",
    "data_folder = \"../dataprofiler/tests/data\"\n",
    "graph_file = \"csv/graph_data_csv_identify.csv\"\n",
    "\n",
    "data = GraphData(os.path.join(data_folder, graph_file))\n",
    "print(data.data.edges)\n",
    "print('=' * 80)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
